package com.my.demo.gatewayService.auth;

import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMapAdapter;
import org.springframework.web.server.ServerWebExchange;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;

@AllArgsConstructor
@Component
@Slf4j
public class AuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {

	private RedisTemplate redisTemplate;
	private RedisConnectionFactory redisConnectionFactory;
	static final String AUTH_TO_RESOURCE = "auth_resource:";

	@Override
	public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {

		ServerWebExchange exchange = authorizationContext.getExchange();
		ServerHttpRequest request = exchange.getRequest();
		String path = request.getURI().getPath();
		
		String authorizationToken = exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION); // 从Header里取出token的值
		if (StringUtils.isBlank(authorizationToken)) {
			authorizationToken = request.getQueryParams().getFirst("access_token");
		}

		if (StringUtils.isBlank(authorizationToken)) {
			log.warn("当前请求头Authorization中的值不存在");
			return Mono.just(new AuthorizationDecision(false));
		}

		RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);
		String token = authorizationToken.replace(OAuth2AccessToken.BEARER_TYPE + " ", "");
		OAuth2Authentication oAuth2Authentication = redisTokenStore.readAuthentication(token);
		Collection<GrantedAuthority> authorities = oAuth2Authentication.getAuthorities(); // 取到角色
		Map<String, List<String>> resourceRolesMap = redisTemplate.opsForHash().entries(AUTH_TO_RESOURCE);
		List<String> pathAuthorities = resourceRolesMap.get(path);
		for (GrantedAuthority authority : authorities) {
			if (pathAuthorities.contains(authority.getAuthority())) {
				return Mono.just(new AuthorizationDecision(true));
			}
		}
		return Mono.just(new AuthorizationDecision(false));

	}

}